<?php

/**
 * 郵便番号データ（大口事業所の個別番号データ）
 */
class ZipBizData
{
    /**
     * 大口事業所個別郵便番号 CSV
     *
     * 提供される CSV データの内容
     *
     * $data[0] = 全国地方公共団体コード(JIS X0401、X0402) ... ag_code varchar(5)
     * $data[1] = 大口事業所名（カナ）
     * $data[2] = 大口事業所名（漢字）
     * $data[3] = 都道府県 ... pref
     * $data[4] = 市区町村 ... town
     * $data[5] = 町域 ... block
     * $data[6] = 小字名、丁目、番地等 ... street
     * $data[7] = 郵便番号(7桁) ... zip_code varchar(7)
     * $data[8] = 旧郵便番号(5桁) ... old_zip_code varchar(5)
     * $data[9] = 取扱い支店名
     * $data[10] = 種別 (「0」は大口事業所、「1」は私書箱)
     * $data[11] = 複数番号 (「0」は複数番号無し、「1」以上は、複数番号のうちの何番目かを示す ... 大口事業所・私書箱は別勘定)
     * $data[12] = 更新の表示（「0」は変更なし、「1」新規追加、「5」廃止（廃止データのみ使用））... changed
     *
     * 以下は、このプログラムが追加する独自のデータ
     *
     * $data[13] = pref_kana
     * $data[14] = town_kana
     * $data[15] = block_kana
     */
    const AG_CODE = 0;
    const COMPANY_NAME_KANA = 1;
    const COMPANY_NAME = 2;
    const PREF = 3;
    const TOWN = 4;
    const BLOCK = 5;
    const STREET = 6;
    const ZIP_CODE = 7;
    const OLD_ZIP_CODE = 8;
    const SHITEN_NAME = 9;
    const TYPE = 10;
    const SERIAL_NO = 11;
    const CHANGED = 12;
    const PREF_KANA = 13;
    const TOWN_KANA = 14;
    const BLOCK_KANA = 15;

    /**
     * @var string CSV ソース・ファイル
     */
    private $srcCsvFile = '';

    /**
     * @var string CSV データ・ファイル
     */
    private $dataCsvFile = '';

    /**
     * @var string SQL ファイル名幹
     */
    private $sqlFileStem = '';

    /**
     * @var int SQL ファイル数
     */
    private $sqlFileCount = 1;

    /**
     * 最大データ長チェック用
     */
    private $maxLength = array(
        self::PREF_KANA => 0,
        self::TOWN_KANA => 0,
        self::BLOCK_KANA => 0,
        self::PREF => 0,
        self::TOWN => 0,
        self::BLOCK => 0,
        self::STREET => 0,
    );

    /**
     * コンストラクタ
     * @param string $srcCsvFile ソースの CSV ファイル名 ... (拡張子は除く)
     */
    public function __construct($srcCsvFile)
    {
        $this->srcCsvFile = $srcCsvFile . '.CSV';
        $srcCsvFile = strtolower($srcCsvFile);
        $this->dataCsvFile = $srcCsvFile . '_zip.csv';
        $this->sqlFileStem = $srcCsvFile . '_';
    }

    /**
     * @return string SQL ファイル名
     */
    private function sqlFileName($fileNo)
    {
        return $this->sqlFileStem . sprintf("%02d", $fileNo) . '.sql';
    }

    /**
     * @return array SQL ファイル名
     */
    public function sqlFileNames()
    {
        $fileNames = array();
        for ($n = 1; $n <= $this->sqlFileCount; $n++) {
            $fileNames[] = $this->sqlFileName($n);
        }
        return $fileNames;
    }

    /**
     * CSV データを正規化する
     * 1) 物理的に2行以上になっているデータを一行にまとめる
     *    （町域カナ、町域が複数行にわたっているデータが存在する）
     * 2) 半角カタカナから全角カタカナへ変換する
     * 3) 町域(カナ)のデータを分析して、町域(カナ) と 町域詳細(カナ) に分割する
     */
    public function normalizeCsvData()
    {
        echo "CSV データ正規化開始 ... \n";
        echo "変換元 CSV ファイル = " . $this->srcCsvFile . "\n";
        echo "変換先 CSV ファイル = " . $this->dataCsvFile . "\n";

        /** @var $srcFile object 変換元 CSV ファイル */
        $srcFile = fopen($this->srcCsvFile, 'r');
        /** @var $srcFile object 変換先 CSV ファイル */
        $dstFile = fopen($this->dataCsvFile, 'w');

        /** @var $lineCountSrc int 変換元ライン数 */
        $lineCountSrc = 0;

        /** @var $kanaDic フリカナ辞書 */
        $kanaDic = new KanaDic;

        while ($line = fgets($srcFile)) {
            $lineCountSrc++;

            // SHIFT JIS --> UTF-8
            $line = mb_convert_encoding(trim($line), 'UTF-8', 'shift_jis');
            // 分割
            $data = explode(',', $line);
            // 引用符と空白を削除
            for ($n = self::AG_CODE; $n <= self::CHANGED; $n++) {
                $data[$n] = trim($data[$n], "\" ");
            }
            // 半角カタカナ --> 全角カタカナ
            $data[self::COMPANY_NAME_KANA] = str_replace('-', 'ー', $data[self::COMPANY_NAME_KANA]);
            $data[self::COMPANY_NAME_KANA] = mb_convert_kana($data[self::COMPANY_NAME_KANA], 'KV', 'UTF-8');
            // 全角英数字 --> 半角英数字
            $data[self::COMPANY_NAME] = str_replace('，', '、', $data[self::COMPANY_NAME]);
            $data[self::COMPANY_NAME] = mb_convert_kana($data[self::COMPANY_NAME], 'as', 'UTF-8');
            $data[self::STREET] = str_replace('，', '、', $data[self::STREET]);
            $data[self::STREET] = mb_convert_kana($data[self::STREET], 'as', 'UTF-8');

            // 振り仮名を辞書から取得
            $data[self::PREF_KANA] = $kanaDic->lookUp('pref', '', $data[self::PREF]);
            $data[self::TOWN_KANA] = $kanaDic->lookUp('town', $data[self::AG_CODE], $data[self::TOWN]);
            $data[self::BLOCK_KANA] = $kanaDic->lookUp('block', $data[self::AG_CODE], $data[self::BLOCK]);

            $this->checkMaxLength($data);
            // 書き出し
            fwrite($dstFile, implode(',', $data) . "\n");
        }

        fclose($srcFile);
        fclose($dstFile);

        echo 'CSV データ変換完了!!' . "\n";
        echo "\n";
        $this->showMaxLengths();
        echo '$lineCountSrc = ' . $lineCountSrc . "\n";
        echo "\n";
    }

    /**
     * 最大データ長をチェック
     * @param $data データ
     */
    private function checkMaxLength($data)
    {
        if (mb_strlen($data[self::PREF_KANA], 'UTF-8') > $this->maxLength[self::PREF_KANA])
            $this->maxLength[self::PREF_KANA] = mb_strlen($data[self::PREF_KANA], 'UTF-8');
        if (mb_strlen($data[self::TOWN_KANA], 'UTF-8') > $this->maxLength[self::TOWN_KANA])
            $this->maxLength[self::TOWN_KANA] = mb_strlen($data[self::TOWN_KANA], 'UTF-8');
        if (mb_strlen($data[self::BLOCK_KANA], 'UTF-8') > $this->maxLength[self::BLOCK_KANA])
            $this->maxLength[self::BLOCK_KANA] = mb_strlen($data[self::BLOCK_KANA], 'UTF-8');
        if (mb_strlen($data[self::PREF], 'UTF-8') > $this->maxLength[self::PREF])
            $this->maxLength[self::PREF] = mb_strlen($data[self::PREF], 'UTF-8');
        if (mb_strlen($data[self::TOWN], 'UTF-8') > $this->maxLength[self::TOWN])
            $this->maxLength[self::TOWN] = mb_strlen($data[self::TOWN], 'UTF-8');
        if (mb_strlen($data[self::BLOCK], 'UTF-8') > $this->maxLength[self::BLOCK])
            $this->maxLength[self::BLOCK] = mb_strlen($data[self::BLOCK], 'UTF-8');
        if (mb_strlen($data[self::STREET], 'UTF-8') > $this->maxLength[self::STREET])
            $this->maxLength[self::STREET] = mb_strlen($data[self::STREET], 'UTF-8');
    }

    /**
     * 最大データ長を表示
     */
    private function showMaxLengths()
    {
        echo '-- データの最大長' . "\n";
        echo '-- $maxLength[pref_kana] = ' . $this->maxLength[self::PREF_KANA] . "\n";
        echo '-- $maxLength[town_kana] = ' . $this->maxLength[self::TOWN_KANA] . "\n";
        echo '-- $maxLength[block_kana] = ' . $this->maxLength[self::BLOCK_KANA] . "\n";
        echo '-- $maxLength[pref] = ' . $this->maxLength[self::PREF] . "\n";
        echo '-- $maxLength[town] = ' . $this->maxLength[self::TOWN] . "\n";
        echo '-- $maxLength[block] = ' . $this->maxLength[self::BLOCK] . "\n";
        echo '-- $maxLength[street] = ' . $this->maxLength[self::STREET] . "\n";
        echo "\n";
    }

    /**
     * INSERT SQL ファイルの作成
     */
    public function createInsertSqlFiles()
    {
        echo "INSERT SQL ファイル書き出し開始 ... \n";

        // 既存の同名ファイルを削除
        $dir = opendir('.');
        $search = '/^' . $this->sqlFileStem . '\d\d.sql$/';
        while ($filename = readdir($dir)) {
            if (preg_match($search, $filename)) {
                unlink($filename);
            }
        }

        $srcFile = fopen($this->dataCsvFile, 'r');
        $this->sqlFileCount = 1;
        $dstFileName = $this->sqlFileName($this->sqlFileCount);
        echo "INSERT SQL ファイル " . $dstFileName . " 書き出し中 ... \n";
        $dstFile = fopen($dstFileName, 'w');

        $lineCount = 0;
        $sqlCount = 0;
        while ($line = fgets($srcFile)) {
            $data = explode(',', trim($line));
            $sqlLine =
                '(' .
                    '"' . $data[self::AG_CODE] . '",' .
                    '"' . $data[self::ZIP_CODE] . '",' .
                    '"' . $data[self::PREF_KANA] . '",' .
                    '"' . $data[self::TOWN_KANA] . '",' .
                    '"' . $data[self::BLOCK_KANA] . '",' .
                    '"' . $data[self::PREF] . '",' .
                    '"' . $data[self::TOWN] . '",' .
                    '"' . $data[self::BLOCK] . '",' .
                    '"' . $data[self::STREET] . '",' .
                    '1,' . // biz
                    $data[self::TYPE] . ',' .
                    $data[self::SERIAL_NO] . ',' .
                    '"' . $data[self::COMPANY_NAME_KANA] . '",' .
                    '"' . $data[self::COMPANY_NAME] . '"' .
                    ')';
            if ($sqlCount > 0) {
                if ($sqlCount < LINES_PER_SQL) {
                    fwrite($dstFile, ",\n");
                } else {
                    fwrite($dstFile, ";\n");
                    $sqlCount = 0;
                }
            }
            if ($lineCount >= LINES_PER_SQL_FILE) {
                fclose($dstFile);
                echo "done. \n";
                $lineCount = 0;
                $sqlCount = 0;
                $this->sqlFileCount++;
                $dstFileName = $this->sqlFileName($this->sqlFileCount);
                echo "INSERT SQL ファイル " . $dstFileName . " 書き出し中 ... \n";
                $dstFile = fopen($dstFileName, 'w');
            }
            if ($sqlCount == 0) {
                fwrite($dstFile,
                    "INSERT INTO `zip_data` (`ag_code`, `zip_code`, `pref_kana`, `town_kana`, `block_kana`, `pref`, `town`, `block`, `street`, `biz`, `biz_type`, `biz_ser`, `company_kana`, `company`) VALUES \n"
                );
            }
            fwrite($dstFile, $sqlLine);
            $lineCount++;
            $sqlCount++;
        }
        fwrite($dstFile, ";\n");
        fclose($dstFile);
        echo "done.\n";
        echo "INSERT SQL ファイル書き出し完了!!\n\n";
    }

    /**
     * DELETE SQL ファイルの作成
     */
    public function createDeleteSqlFiles()
    {
        echo "DELETE SQL ファイル書き出し開始 ... \n";

        // 既存の同名ファイルを削除
        $dir = opendir('.');
        $search = '/^' . $this->sqlFileStem . '\d\d.sql$/';
        while ($dstFileName = readdir($dir)) {
            if (preg_match($search, $dstFileName)) {
                unlink($dstFileName);
            }
        }

        $srcFile = fopen($this->dataCsvFile, 'r');
        $this->sqlFileCount = 1;
        $dstFileName = $this->sqlFileName($this->sqlFileCount);
        echo "DELETE SQL ファイル " . $dstFileName . " 書き出し中 ... \n";
        $dstFile = fopen($dstFileName, 'w');

        $lineCount = 0;
        while ($line = fgets($srcFile)) {
            $data = explode(',', trim($line));
            $sqlLine = 'DELETE FROM `zip_data` WHERE ' .
                '`zip_code` = "' . $data[self::ZIP_CODE] . '" AND ' .
                '`pref` = "' . $data[self::PREF] . '" AND ' .
                '`town` = "' . $data[self::TOWN] . '" AND ' .
                '`block` = "' . $data[self::BLOCK] . '" AND ' .
                '`street` = "' . $data[self::STREET] . '";' . "\n";
            if ($lineCount >= LINES_PER_SQL_FILE) {
                fclose($dstFile);
                echo "done. \n";
                $lineCount = 0;
                $$this->sqlFileCount++;
                $dstFileName = $this->sqlFileName($this->sqlFileCount);
                echo "DELETE SQL ファイル " . $dstFileName . " 書き出し中 ... \n";
                $dstFile = fopen($dstFileName, 'w');
            }
            fwrite($dstFile, $sqlLine);
            $lineCount++;
        }
        fclose($dstFile);
        echo "done.\n";
        echo "DELETE SQL ファイル書き出し完了!!\n\n";
    }
}